﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Co9320.NamaAlert.Engine.Inspector;
using Co9320.NamaAlert.Engine.Lists;
using Co9320.NamaAlert.Model;
using Co9320.NamaAlert.Engine.DebugHelper;

namespace Co9320.NamaAlert.Engine
{
	public enum CrawlingMode
	{
		Normal,
		NormalWithoutClosedNotification,
		Sleep
	}


	internal class LiveProgramNotification : IDisposable
	{
		/// <summary>
		/// RssFeederEngine.
		/// </summary>
		private Feeder.NicoLiveLiveInfoFeeder Feeder_;

		private OnAirListBuilder OnAirDetector_;
		private InspectionListBuilder Inspection_;
		private InternalLiveList LiveList_;
		private Thread Worker_;
		private ThreadStart threadProcess_;
		private AutoResetEvent Gate_;

		private LiveNotification Parent_;

		private readonly object ModeSyncRoot_ = new object();

		private CrawlingMode CrawlingMode_;
		private TimeSpan Interval_;
		private bool IsStarted_;

		private static readonly TimeSpan ErrorDelay = new TimeSpan(0, 1, 0);
		private static readonly TimeSpan DefaultInterval = new TimeSpan(0, 0, 20);

		private string BaseUriFormat_;

		public LiveProgramNotification(LiveNotification parent, string baseUri, string userAgent)
		{
			Debug.Assert(parent != null);

			Parent_ = parent;

			Feeder_ = new Feeder.NicoLiveLiveInfoFeeder(baseUri, userAgent);
			BaseUriFormat_ = baseUri;

			LiveList_ = new InternalLiveList(this);
			OnAirDetector_ = new OnAirListBuilder(0, 0.9d, LiveList_, Feeder_);
			Inspection_ = new InspectionListBuilder(1, LiveList_, Feeder_);
			threadProcess_ = new ThreadStart(ThreadProc);
			Gate_ = new AutoResetEvent(true);

			Worker_ = new Thread(threadProcess_);
			Worker_.Name = "LiveNotificatorWorker";
			Worker_.IsBackground = true;

			CrawlingMode_ = CrawlingMode.Normal;
			Interval_ = DefaultInterval;

			IsStarted_ = false;

			this.CurrentProgramCount = Define.InvalidValue;
		}

		public event EventHandler FirstCrawlingComplted
		{
			add
			{
				OnAirDetector_.FirstCrawlingComplted += value;
			}
			remove
			{
				OnAirDetector_.FirstCrawlingComplted -= value;
			}
		}


		public void Start()
		{
			Start(CrawlingMode.Normal);
		}


		public void Start(CrawlingMode mode)
		{
			Start(mode, null, true);
		}

		public void Start(CrawlingMode mode, IList<LiveProgram> programList, bool crawlAllPages)
		{
			lock (ModeSyncRoot_)
			{
				if (IsStarted_)
				{
					return;
				}

				this.ChangeCrawlingMode(mode);

				if (programList != null)
				{
					for (int i = 0; i < programList.Count; i++)
					{
						LiveList_.AddProgram(programList[i],false);
					}
				}

				if (!crawlAllPages)
				{
					OnAirDetector_.RepressionReInitialize();
				}

				Worker_.Start();
				IsStarted_ = true;
			}
		}

		public void Start(IList<LiveProgram> programList)
		{
			Start(CrawlingMode.Normal, programList, true);
		}

		public void Start(IList<LiveProgram> programList, bool crawlAllPages)
		{
			Start(CrawlingMode.Normal, programList, crawlAllPages);
		}

		public void Start(CrawlingMode mode, IList<LiveProgram> programList)
		{
			Start(mode, programList, true);
		}

		public bool AddProgram(LiveProgram Program)
		{
			System.Diagnostics.Debug.Assert(Program != null);
			bool ret = LiveList_.AddProgram(Program, true);
			return ret;
		}



		private void ThreadProc()
		{
				CrawlingMode Mode;
				TimeSpan GateInterval;
				try
				{
					for (; ; )
					{
						lock (ModeSyncRoot_)
						{
							Mode = this.CrawlingMode_;
							GateInterval = this.Interval_;

							if (Feeder_.BaseUri != this.BaseUriFormat_)
							{
								Feeder_.ChangeURL(this.BaseUriFormat_);
							}
						}

						LogOutputter.OutputConsole("end program detector finish and wait.");
						LogOutputter.OutputConsole();
						LogOutputter.OutputConsole();


						Gate_.WaitOne(GateInterval, false);


						LogOutputter.OutputConsole("Start program detector start");




						if (Mode != CrawlingMode.Sleep)
						{
							try
							{
								this.CurrentProgramCount = OnAirDetector_.RoutineWork();
							}
							catch (Exception ex)
							{
								LogOutputter.WriteElement(LogMode.Error, ex);
								LogOutputter.OutputConsole("DetectException:{0}", ex.GetType().ToString());
								Thread.Sleep(ErrorDelay);
								OnAirDetector_.RequestReInitialize();
								Parent_.InvokeSendSystemMessage(new SystemMessageEventArgs(SystemMessageKind.Error, "Can't connect the Rss Server"));
							}
						}



						LogOutputter.OutputConsole("Start program detector finish and Wait.");
						LogOutputter.OutputConsole();


						Gate_.WaitOne(GateInterval, false);


						LogOutputter.OutputConsole("end program detector start");


						switch (Mode)
						{
							case CrawlingMode.Normal:

								try
								{
									this.CurrentProgramCount = Inspection_.Routine();
								}
								catch (Exception ex)
								{
									LogOutputter.WriteElement(LogMode.Error, ex);
									LogOutputter.OutputConsole("DetectException:{0}", ex.GetType().ToString());
									Thread.Sleep(ErrorDelay);
									Parent_.InvokeSendSystemMessage(new SystemMessageEventArgs(SystemMessageKind.Error, "Can't connect the Rss Server"));
								}
								break;

							case CrawlingMode.NormalWithoutClosedNotification:
								AlternativeInspection();
								break;

							case CrawlingMode.Sleep:
								break;

							default:
								throw new NotImplementedException();
						}

					}
				}
				catch(ThreadInterruptedException)
				{
					LogOutputter.OutputConsole("Thread Exit");
				}

		}

		public int CurrentProgramCount
		{
			get;
			private set;
		}


		private void AlternativeInspection()
		{
			LiveList_.Inspect();
		}

		internal void SendStateChanged(IList<StateChangedEventArgs> EventArgList)
		{
			Parent_.SendStateChanged(EventArgList);
		}


		public void ChangeCrawlingMode(CrawlingMode mode)
		{
			LogOutputter.OutputConsole("Changing Mode:{0}", mode);

			lock (ModeSyncRoot_)
			{
				if (mode == CrawlingMode.Sleep)
				{
					Interval_ = new TimeSpan(0, 0, 0, 0, -1);
				}
				else
				{
					Interval_ = DefaultInterval;
				}


				var Recent = this.CrawlingMode_;
				this.CrawlingMode_ = mode;

				if (Recent == CrawlingMode.Sleep && mode != CrawlingMode.Sleep)
				{
					Gate_.Set();
				}
			}


			LogOutputter.OutputConsole("ChangedMode:{0}", mode);

		}

		public CrawlingMode CrawlingMode
		{
			get
			{
				return CrawlingMode_;
			}
		}

		public string BaseUri
		{
			get
			{
				lock (ModeSyncRoot_)
				{
					return BaseUriFormat_;
				}
			}
			set
			{
				if (value != null)
				{
					lock (ModeSyncRoot_)
					{
						BaseUriFormat_ = value;
					}
				}
			}
		}


		#region ISystemMessageSender メンバ


		#endregion

		#region IDisposable メンバ

		public void Dispose()
		{
			Worker_.Interrupt();
		}

		#endregion


	}
}
